#include "sbrush_pantomime.h"
#include <fstream>

using namespace sbrush;
using namespace dag;
using namespace std;

namespace {
	void spectral_model_finished(const Player* player, void* userData) {
		PantomimeRecorder* archiver = static_cast<PantomimeRecorder*>(userData);
		if (archiver != NULL) {
			archiver->setFinished();
		}
	}
}


PantomimeError::PantomimeError(const string& msg)
:	AcsError(msg)
{
}

PantomimeError::~PantomimeError() throw() {
}

//DuplicateTimestamp::DuplicateTimestamp() 
//:	PantomimeError("duplicate timestamp")
//{
//}
//
//DuplicateTimestamp::~DuplicateTimestamp() throw() {
//}

Sample::Sample() 
:	smsFrame(0)
{
}

Sample::Sample(unsigned frame, const InstrumentState& state) 
:	instrumentState(state),
	smsFrame(frame)
{
}

Sample::~Sample() throw() {
}

auto_ptr<Sample> Sample::inflate(istream& binaryStream) {
	auto_ptr<InstrumentState> state;
	unsigned frame;
	binaryStream.read((char*)&frame, sizeof(frame));
	state = InstrumentState::inflate(binaryStream);
	return auto_ptr<Sample>(new Sample(frame, *state));
}

void Sample::serialize(ostream& binaryStream) const {
	binaryStream.write((const char*)&smsFrame, sizeof(smsFrame));
	instrumentState.serialize(binaryStream);
}


Pantomime::Pantomime(const SpectralModel& spectralModel)
:	m_SpectralModel(spectralModel)
{
}

Pantomime::~Pantomime() {
	for (size_t i = 0; i < m_Samples.size(); i++) {
		delete m_Samples[i];
	}
}
	
size_t Pantomime::size() const {
	return m_Samples.size();
}

void Pantomime::add(const Sample& sample) {
	auto_ptr<Sample> sampleCopy(new Sample(sample));
	add(sampleCopy);
}
	
void Pantomime::add(auto_ptr<Sample> sample) {
	m_Samples.push_back(sample.release());
}
	

const Sample& Pantomime::at(size_t idx) const {
	return *(m_Samples[idx]);
}

const SpectralModel& Pantomime::getSpectralModel() const {
	return m_SpectralModel;
}

auto_ptr<Pantomime> Pantomime::read(const char* filename) {
	ifstream ifs(filename, ios::binary);
	ifs.exceptions(ios::badbit);
	return Pantomime::inflate(ifs);
}

bool Pantomime::write(const char* filename) const {
	ofstream ofs(filename, ios::binary | ios::trunc);
	serialize(ofs);
	return (ofs.is_open() && ofs.good());
}

auto_ptr<Pantomime> Pantomime::inflate(istream& binaryStream) {
	auto_ptr<Pantomime> newPantomime(NULL);
	auto_ptr<SpectralModel> model = SpectralModel::inflate(binaryStream);
	if (model.get() == NULL) {
		return newPantomime;
	}
	
	newPantomime.reset(new Pantomime(*model));
	unsigned numSamples = 0;
	binaryStream.read((char*)&numSamples, sizeof(numSamples));
	for (unsigned i = 0; i < numSamples; i++) {
		auto_ptr<Sample> sample = Sample::inflate(binaryStream);
		newPantomime->add(sample);
	}
	
	return newPantomime;
}

void Pantomime::serialize(ostream& binaryStream) const {
	m_SpectralModel.serialize(binaryStream);
	unsigned numSamples = m_Samples.size();
	binaryStream.write((const char*)&numSamples, sizeof(numSamples));
	for (unsigned i = 0; i < numSamples; i++) {
		m_Samples[i]->serialize(binaryStream);
	}
}


PantomimeRecorder::PantomimeRecorder() 
:	m_DoCapture(false),
	m_Callback(NULL),
	m_UserData(NULL),
	m_SpectralModel(NULL)
{
	m_Player.setFinishedCallback(spectral_model_finished, this);
}

PantomimeRecorder::~PantomimeRecorder() throw() {
	clear();
}

void PantomimeRecorder::setCallback(pantomime_recorder_cb callback, 
									void* userData) {
	m_Callback = callback;
	m_UserData = userData;
}

void PantomimeRecorder::start(const SpectralModel& model,
								unsigned countdown, 
								double countdownDelta){
	if (m_Callback != NULL) {
		m_Callback(this, Pantomime_Prepare, m_UserData);
	}
	
	{
		ScopedLock sl(m_StateMutex);
		m_SpectralModel = &model;
		m_Player.setModel(model);
	}
	
	m_CountDownThread.startCountdown(*this, 
									 countdown, 
									 countdownDelta, 
									 m_Callback, 
									 m_UserData);
}

void PantomimeRecorder::setFinished() {
	{
		ScopedLock sl(m_StateMutex);
		m_DoCapture = false;
	}
	if (m_Callback != NULL) {
		m_Callback(this, Pantomime_Finish, m_UserData);
	}	
}

void PantomimeRecorder::cancel() {
	{
		ScopedLock sl(m_StateMutex);
		m_DoCapture = false;
	}
	
	if (m_Callback != NULL) {
		m_Callback(this, Pantomime_Cancelled, m_UserData);
	}
}

auto_ptr<Pantomime> PantomimeRecorder::getPantomime() const {
	ScopedLock sl(m_StateMutex);
	if (m_SpectralModel == NULL) {
		return auto_ptr<Pantomime>(NULL);
	}
	auto_ptr<Pantomime> pantomime(new Pantomime(*m_SpectralModel));
	
	map<size_t, InstrumentState*>::const_iterator iter;
	for (iter = m_Frame2State.begin(); iter != m_Frame2State.end(); iter++) {
		auto_ptr<Sample> sample(new Sample(iter->first, *(iter->second)));
		pantomime->add(sample);
	}
	return pantomime;
}

void PantomimeRecorder::instrumentCallback(const Instrument* instrument,
										   const InstrumentState& state) {
	ScopedTryLock sl(m_StateMutex);
	if (sl.success()) {
		if (m_DoCapture) {
			int curFrame = m_Player.playHead();
			InstrumentState* newState = new InstrumentState(state);
			m_States.insert(newState);
			m_Frame2State[curFrame] = newState;
		}
	}
}

void PantomimeRecorder::clear() {
	ScopedLock sl(m_StateMutex);
	
	m_DoCapture = false;
	set<InstrumentState*>::iterator iter;
	for (iter = m_States.begin(); iter != m_States.end(); iter++) {
		delete *iter;
	}
	m_States.clear();
	m_Frame2State.clear();
	m_SpectralModel = NULL;
}


void PantomimeRecorder::startRecording() {
	ScopedLock sl(m_StateMutex);
	if (m_Callback != NULL) {
		m_Callback(this, Pantomime_Begin, m_UserData);
	}
	m_Player.start();
	m_DoCapture = true;
}

PantomimeRecorder::CountdownThread::CountdownThread()
:	m_CountDown(0),
	m_CountDownDelta(0.0),
	m_Recorder(NULL),
	m_Callback(NULL),
	m_UserData(NULL)
{
}

PantomimeRecorder::CountdownThread::~CountdownThread() throw() {
}

void PantomimeRecorder::CountdownThread::startCountdown(PantomimeRecorder& recorder,
														unsigned countdown,
														double countdownDelta,
														pantomime_recorder_cb callback,
														void* userData) {
	m_CountDownDelta = countdownDelta;
	m_CountDown = countdown;
	m_Recorder = &recorder;
	m_Callback = callback;
	m_UserData = userData;
	startThread();
}

void PantomimeRecorder::CountdownThread::runThread() {

	while (m_CountDown != 0) {
		if (m_Callback != NULL) {
			m_Callback(m_Recorder, Pantomime_Countdown, m_UserData);
		}
		dag_msleep(m_CountDownDelta * 1000.0);
		m_CountDown--;
	}
	if (m_Callback != NULL) {
		m_Callback(m_Recorder, Pantomime_Countdown, m_UserData);
	}
	dag_msleep(m_CountDownDelta * 1000.0);
	
	m_Recorder->startRecording();
}
